﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Moonlite.Languages;
using Moonlite.Languages.vJass;
using Moonlite.Languages.vJass.CodeObj;
using Moonlite.Languages.vJass.Collections;

namespace Moonlite.Languages.vJass
{
    public class JParser : IParser
    {
        public string Text { get; set; }
        public List<TreeNode> Output { get; private set; }
        public bool ZincEnabled { get; set; }

        private bool vjassEnabled = true;
        public bool vJassEnabled
        {
            get { return vjassEnabled; }
            set { vjassEnabled = value; }
        }

        public LibraryCollection Libraries { get; private set; }
        public ScopeCollection Scopes { get; private set; }
        public StructCollection Structs { get; private set; }
        public FunctionCollection Functions { get; private set; }
        public GlobalsCollection Globals { get; private set; }

        private bool IsInCommentBlock { get; set; }

        public void Parse()
        {
            Parse(Text);
        }

        public void Parse(string text)
        {
            if (string.IsNullOrEmpty(text) == true)
            {
                throw new ArgumentNullException();
            }

            Output.Clear();

            Libraries.Clear();
            Globals.Clear();
            Functions.Clear();
            Scopes.Clear();
            Structs.Clear();

            text = RemoveComments(text);
            ParseFunctions(text, 0);
            ParseGlobals(text, 0);
            ParseLibraries(text, true, 0);
            ParseScopes(text, true, 0);
            ParseStructs(text, true, 0);
        }

        internal LibraryCollection ParseLibraries(string text, bool parseChildren, int currentIndex)
        {
            LibraryCollection listFoundLibraries = new LibraryCollection();

            string libPattern = vjassEnabled == true ? RegularExpressions.Library : "";
            string libEndPattern = ZincEnabled == true ? RegularExpressions.LibraryZincEnd : vjassEnabled == true ? RegularExpressions.LibraryEnd : "";

            bool findingLibEnd = false;
            Library foundLibrary = new Library();

            bool mayExec = true;
            int brackets = 0;
            int startMatchLength = 0;

            bool buildString = false;
            StringBuilder textBetween = new StringBuilder();

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);

            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingLibEnd == false)
                {
                    Match libraryMatch = Regex.Match(line, libPattern);

                    if (libraryMatch.Success == true)
                    {
                        foundLibrary.Name = libraryMatch.Groups["name"].Value;
                        foundLibrary.Inherits = libraryMatch.Groups["inherits"].Value;
                        foundLibrary.StartLine = currentLineIndex;

                        startMatchLength = libraryMatch.Length;
                        textBetween.Append(line);
                        textBetween.AppendLine();

                        buildString = true;
                        findingLibEnd = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || (line.Trim().StartsWith("//") == true))
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match endLibraryMatch = Regex.Match(line, libEndPattern);
                        if (endLibraryMatch.Success == true)
                        {
                            foundLibrary.EndLine = currentLineIndex;

                            findingLibEnd = false;
                            buildString = false;

                            textBetween.Append(line);
                            textBetween.AppendLine();
                            Libraries.Add(foundLibrary);
                            listFoundLibraries.Add(foundLibrary);

                            TreeNode node = new TreeNode { Tag = foundLibrary, Text = String.Format("{0} (library)", foundLibrary.Name) };
                            Output.Add(node);

                            if (parseChildren == true)
                            {
                                GlobalsCollection foundGlobals = ParseGlobals(textBetween.ToString(), foundLibrary.StartLine);
                                FunctionCollection foundFunctions = ParseFunctions(textBetween.ToString(), foundLibrary.StartLine);
                                StructCollection foundStructs = ParseStructs(textBetween.ToString(), true, foundLibrary.StartLine);
                                ScopeCollection foundScopes = ParseScopes(textBetween.ToString(), true, foundLibrary.StartLine);

                                foundGlobals.ForEach(foundGlobalsBlock =>
                                {
                                    foundGlobalsBlock.Parent = foundLibrary;
                                    TreeNode globalNode = new TreeNode { Tag = foundGlobalsBlock, Text = "(globals)" };
                                    foundLibrary.Globals.Add(foundGlobalsBlock);
                                    Output.Add(globalNode);
                                });

                                foundFunctions.ForEach(foundFunction =>
                                {
                                    foundFunction.Parent = foundLibrary;
                                    if ((foundFunction.Modifiers & Modifiers.Public) != 0)
                                    {
                                        foundFunction.Name = foundLibrary.Name + "_" + foundFunction.Name;
                                    }

                                    TreeNode functionNode = new TreeNode { Tag = foundFunction, Text = String.Format("{0} (function)", foundFunction.Name) };
                                    foundLibrary.Functions.Add(foundFunction);
                                    Output.Add(functionNode);
                                });

                                foundStructs.ForEach(foundStruct =>
                                {
                                    foundStruct.Parent = foundLibrary;
                                    TreeNode structNode = new TreeNode { Tag = foundStruct, Text = String.Format("{0} (struct)", foundStruct.Name) };
                                    foundLibrary.Structs.Add(foundStruct);
                                    Output.Add(structNode);
                                });

                                foundScopes.ForEach(foundScope =>
                                {
                                    foundScope.Parent = foundLibrary;
                                    TreeNode scopeNode = new TreeNode { Tag = foundScope, Text = String.Format("{0} (scope)", foundScope.Name) };
                                    foundLibrary.Scopes.Add(foundScope);
                                    Output.Add(scopeNode);
                                });
                            }

                            foundLibrary = new Library();
                            textBetween = new StringBuilder();
                        }
                    }

                    if (buildString == true)
                    {
                        textBetween.Append(line);
                        textBetween.AppendLine();
                    }

                    mayExec = true;
                }

                currentLineIndex++;
            });

            return listFoundLibraries;
        }

        internal ScopeCollection ParseScopes(string text, bool parseChildren, int currentIndex)
        {
            ScopeCollection listFoundScopes = new ScopeCollection();

            string scopePattern = vjassEnabled == true ? RegularExpressions.Scope : "";
            string scopeEndPattern = ZincEnabled == true ? RegularExpressions.ScopeZincEnd : vjassEnabled == true ? RegularExpressions.ScopeEnd : "";

            bool findingScopeEnd = false;
            Scope foundScope = new Scope();

            bool mayExec = true;
            int brackets = 0;
            int startMatchLength = 0;

            bool buildString = false;
            StringBuilder textBetween = new StringBuilder();

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);

            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingScopeEnd == false)
                {
                    Match scopeMatch = Regex.Match(line, scopePattern);

                    if (scopeMatch.Success == true)
                    {
                        foundScope.Name = scopeMatch.Groups["name"].Value;
                        foundScope.StartLine = currentLineIndex;

                        startMatchLength = scopeMatch.Length;
                        textBetween.Append(line);
                        textBetween.AppendLine();

                        buildString = true;
                        findingScopeEnd = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || (line.Trim().StartsWith("//") == true))
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match endScopeMatch = Regex.Match(line, scopeEndPattern);
                        if (endScopeMatch.Success == true)
                        {
                            foundScope.EndLine = currentLineIndex;

                            findingScopeEnd = false;
                            buildString = false;

                            textBetween.Append(line);
                            textBetween.AppendLine();
                            Scopes.Add(foundScope);
                            listFoundScopes.Add(foundScope);

                            TreeNode node = new TreeNode { Tag = foundScope, Text = String.Format("{0} (scope)", foundScope.Name) };
                            Output.Add(node);

                            if (parseChildren == true)
                            {
                                GlobalsCollection foundGlobals = ParseGlobals(textBetween.ToString(), foundScope.StartLine);
                                FunctionCollection foundFunctions = ParseFunctions(textBetween.ToString(), foundScope.StartLine);
                                StructCollection foundStructs = ParseStructs(textBetween.ToString(), true, foundScope.StartLine);

                                foundGlobals.ForEach(foundGlobalsBlock =>
                                {
                                    foundGlobalsBlock.Parent = foundScope;
                                    TreeNode globalNode = new TreeNode { Tag = foundGlobalsBlock, Text = "(globals)" };
                                    foundScope.Globals.Add(foundGlobalsBlock);
                                    Output.Add(globalNode);
                                });

                                foundFunctions.ForEach(foundFunction =>
                                {
                                    foundFunction.Parent = foundScope;
                                    if ((foundFunction.Modifiers & Modifiers.Public) != 0)
                                    {
                                        foundFunction.Name = foundScope.Name + "_" + foundFunction.Name;
                                    }

                                    TreeNode functionNode = new TreeNode { Tag = foundFunction, Text = String.Format("{0} (function)", foundFunction.Name) };
                                    foundScope.Functions.Add(foundFunction);
                                    Output.Add(functionNode);
                                });

                                foundStructs.ForEach(foundStruct =>
                                {
                                    foundStruct.Parent = foundScope;
                                    TreeNode structNode = new TreeNode { Tag = foundStruct, Text = String.Format("{0} (struct)", foundStruct.Name) };
                                    foundScope.Structs.Add(foundStruct);
                                    Output.Add(structNode);
                                });
                            }

                            foundScope = new Scope();
                            textBetween = new StringBuilder();
                        }
                    }

                    if (buildString == true)
                    {
                        textBetween.Append(line);
                        textBetween.AppendLine();
                    }

                    mayExec = true;
                }

                currentLineIndex++;
            });

            return listFoundScopes;
        }

        internal GlobalsCollection ParseGlobals(string text, int currentIndex)
        {
            GlobalsCollection listFoundGlobals = new GlobalsCollection();

            string globalsPattern = RegularExpressions.Globals;
            string globalsEndPattern = ZincEnabled == true ? RegularExpressions.GlobalsEndZinc : RegularExpressions.GlobalsEnd;

            bool findingGlobalsEnd = false;
            Globals foundGlobals = new Globals();

            string globalVariablePattern = ZincEnabled == true ? RegularExpressions.VariableGlobalZinc : RegularExpressions.VariableGlobal;

            StringBuilder textBetween = new StringBuilder();
            bool buildText = false;

            bool mayExec = true;
            int brackets = 0;

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingGlobalsEnd == false)
                {
                    Match globalsMatch = Regex.Match(line, globalsPattern);

                    if (globalsMatch.Success == true)
                    {
                        foundGlobals.StartLine = currentLineIndex;
                        findingGlobalsEnd = true;
                        buildText = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || line.Trim().StartsWith("//") == true)
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match varMatch = Regex.Match(line, globalVariablePattern);

                        if (varMatch.Success == true)
                        {
                            GlobalVariable variable = new GlobalVariable { StartLine = currentLineIndex, EndLine = currentLineIndex, Name = varMatch.Groups["name"].Value, Type = varMatch.Groups["type"].Value, Value = varMatch.Groups["value"].Value };
                            bool isPublic = false;
                            bool isPrivate = false;

                            if (varMatch.Groups["public"].Value == "public")
                            {
                                variable.Modifiers = Modifiers.Public;
                                isPublic = true;
                            }

                            if (varMatch.Groups["private"].Value == "private")
                            {
                                variable.Modifiers = isPublic == true ? Modifiers.Public | Modifiers.Private : Modifiers.Private;
                                isPrivate = true;
                            }

                            if (varMatch.Groups["constant"].Value == "constant")
                            {
                                variable.Modifiers = isPublic == true && isPrivate == true ? Modifiers.Public |
                                    Modifiers.Private | Modifiers.Constant : isPrivate == true ?
                                    Modifiers.Private | Modifiers.Constant : isPublic == true ?
                                    Modifiers.Public | Modifiers.Constant : Modifiers.Constant;
                            }

                            foundGlobals.GlobalVariables.Add(variable);
                        }

                        Match endGlobalsMatch = Regex.Match(line, globalsEndPattern);

                        if (endGlobalsMatch.Success == true)
                        {
                            foundGlobals.EndLine = currentLineIndex;
                            findingGlobalsEnd = false;
                            buildText = false;

                            textBetween = new StringBuilder();
                            listFoundGlobals.Add(foundGlobals);

                            Globals.Add(foundGlobals);
                            foundGlobals = new Globals();
                        }
                    }
                    mayExec = true;
                }

                currentLineIndex++;

                if (buildText == true)
                {
                    textBetween.Append(line);
                    textBetween.AppendLine();
                }
            });

            return listFoundGlobals;
        }

        internal FunctionCollection ParseFunctions(string text, int currentIndex)
        {
            FunctionCollection listFoundFunctions = new FunctionCollection();

            string functionPattern = ZincEnabled == true ? RegularExpressions.FunctionZinc : RegularExpressions.Function;
            string functionEndPattern = ZincEnabled == true ? RegularExpressions.FunctionEndZinc : RegularExpressions.FunctionEnd;

            bool findingFunctionEnd = false;
            Function foundFunction = new Function();

            string functionVariablePattern = ZincEnabled == true ? RegularExpressions.VariableLocalZinc : RegularExpressions.VariableLocal;

            StringBuilder textBetween = new StringBuilder();
            bool buildText = false;

            bool mayExec = true;
            int brackets = 0;

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingFunctionEnd == false)
                {
                    Match functionMatch = Regex.Match(line, functionPattern);

                    if (functionMatch.Success == true)
                    {
                        foundFunction.StartLine = currentLineIndex;
                        foundFunction.Name = functionMatch.Groups["name"].Value;
                        foundFunction.Arguments = functionMatch.Groups["arguments"].Value;
                        foundFunction.Returns = functionMatch.Groups["returns"].Value;

                        string[] arguments = foundFunction.Arguments.Split(',');
                        Array.ForEach(arguments, argument =>
                        {
                            argument = argument.Trim();
                            if (argument != "nothing" && argument != "void")
                            {
                                argument = argument.Replace("  ", " ");
                                string[] splittedArgument = argument.Split(' ');
                                if (splittedArgument.Length == 2)
                                {
                                    foundFunction.Variables.Add(new FunctionVariable
                                    {
                                        StartLine = currentLineIndex,
                                        EndLine = currentLineIndex,
                                        Parent = foundFunction,
                                        Argument = true,
                                        Value = "",
                                        Type = splittedArgument[0],
                                        Name = splittedArgument[1]
                                    });
                                }
                            }
                        });

                        bool isPublic = false;
                        bool isPrivate = false;

                        if (functionMatch.Groups["public"].Value == "public")
                        {
                            foundFunction.Modifiers = Modifiers.Public;
                            isPublic = true;
                        }

                        if (functionMatch.Groups["private"].Value == "private")
                        {
                            foundFunction.Modifiers = isPublic == true ? Modifiers.Public | Modifiers.Private : Modifiers.Private;
                            isPrivate = true;
                        }

                        if (functionMatch.Groups["constant"].Value == "constant")
                        {
                            foundFunction.Modifiers = isPublic == true && isPrivate == true ? Modifiers.Public |
                                Modifiers.Private | Modifiers.Constant : isPrivate == true ? Modifiers.Private |
                                Modifiers.Constant : isPublic == true ? Modifiers.Public | Modifiers.Constant :
                                Modifiers.Constant;
                        }

                        findingFunctionEnd = true;
                        buildText = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || (line.Trim().StartsWith("//") == true))
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match varMatch = Regex.Match(line, functionVariablePattern);

                        if (varMatch.Success == true)
                        {
                            FunctionVariable variable = new FunctionVariable { StartLine = currentLineIndex, EndLine = currentLineIndex, Name = varMatch.Groups["name"].Value, Type = varMatch.Groups["type"].Value, Value = varMatch.Groups["value"].Value };
                            foundFunction.Variables.Add(variable);
                        }

                        Match endFunctionMatch = Regex.Match(line, functionEndPattern);

                        if (endFunctionMatch.Success == true)
                        {
                            foundFunction.EndLine = currentLineIndex;
                            findingFunctionEnd = false;
                            buildText = false;

                            textBetween = new StringBuilder();
                            listFoundFunctions.Add(foundFunction);

                            Functions.Add(foundFunction);
                            foundFunction = new Function();
                        }
                    }

                    mayExec = true;
                }

                currentLineIndex++;

                if (buildText == true)
                {
                    textBetween.Append(line);
                    textBetween.AppendLine();
                }
            });

            return listFoundFunctions;
        }

        internal StructCollection ParseStructs(string text, bool parseChildren, int currentIndex)
        {
            StructCollection listFoundStructs = new StructCollection();

            string structPattern = vjassEnabled == true ? RegularExpressions.Struct : "";
            string structEndPattern = ZincEnabled == true ? RegularExpressions.StructZincEnd : vjassEnabled == true ? RegularExpressions.StructEnd : "";

            bool findingStructEnd = false;
            Struct foundStruct = new Struct();

            bool mayExec = true;
            int brackets = 0;
            int startMatchLength = 0;

            bool buildString = false;
            StringBuilder textBetween = new StringBuilder();

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingStructEnd == false)
                {
                    Match structMatch = Regex.Match(line, structPattern);

                    if (structMatch.Success == true)
                    {
                        foundStruct.Name = structMatch.Groups["name"].Value;
                        foundStruct.Extends = structMatch.Groups["extends"].Value;
                        foundStruct.StartLine = currentLineIndex;

                        startMatchLength = structMatch.Length;
                        textBetween.Append(line);
                        textBetween.AppendLine();

                        buildString = true;
                        findingStructEnd = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || (line.Trim().StartsWith("//") == true))
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match endStructMatch = Regex.Match(line, structEndPattern);

                        if (endStructMatch.Success == true)
                        {
                            foundStruct.EndLine = currentLineIndex;
                            findingStructEnd = false;
                            buildString = false;

                            textBetween.Append(line);
                            textBetween.AppendLine();
                            Structs.Add(foundStruct);
                            listFoundStructs.Add(foundStruct);

                            TreeNode node = new TreeNode { Tag = foundStruct, Text = String.Format("{0} (struct)", foundStruct.Name) };
                            Output.Add(node);

                            if (parseChildren == true)
                            {
                                MethodCollection foundMethods = ParseMethods(textBetween.ToString(), foundStruct.StartLine);
                                List<int> occupiedLines = new List<int>();

                                occupiedLines.Add(foundStruct.StartLine);
                                occupiedLines.Add(foundStruct.EndLine);

                                foundMethods.ForEach(foundMethod =>
                                {
                                    foundMethod.Parent = foundStruct;
                                    TreeNode methodNode = new TreeNode { Tag = foundMethod, Text = String.Format("{0} (method)", foundMethod.Name) };
                                    foundStruct.Methods.Add(foundMethod);
                                    Output.Add(methodNode);
                                    occupiedLines.Add(foundMethod.StartLine);
                                    occupiedLines.Add(foundMethod.EndLine);

                                    for (int lineIndex = foundMethod.StartLine + 1; lineIndex < foundMethod.EndLine; lineIndex++)
                                    {
                                        occupiedLines.Add(lineIndex);
                                    }
                                });

                                MemberCollection foundMembers = ParseMembers(textBetween.ToString(), foundStruct.StartLine);

                                foundMembers.ForEach(foundMember =>
                                {
                                    if (occupiedLines.Contains(foundMember.StartLine) == false)
                                    {
                                        foundMember.Parent = foundStruct;
                                        TreeNode memberNode = new TreeNode() { Tag = foundMember, Text = String.Format("{0} (member)", foundMember.Name) };
                                        foundStruct.Members.Add(foundMember);
                                        Output.Add(memberNode);
                                    }
                                });
                            }

                            foundStruct = new Struct();
                            textBetween = new StringBuilder();
                        }
                    }

                    if (buildString == true)
                    {
                        textBetween.Append(line);
                        textBetween.AppendLine();
                    }

                    mayExec = true;
                }

                currentLineIndex++;
            });

            return listFoundStructs;
        }

        internal MethodCollection ParseMethods(string text, int currentIndex)
        {
            MethodCollection listFoundMethods = new MethodCollection();

            string methodPattern = ZincEnabled == true ? RegularExpressions.MethodZinc : RegularExpressions.Method;
            string methodEndPattern = ZincEnabled == true ? RegularExpressions.MethodEndZinc : RegularExpressions.MethodEnd;

            bool findingMethodEnd = false;
            Method foundMethod = new Method();

            string methodVariablePattern = ZincEnabled == true ? RegularExpressions.VariableLocalZinc : RegularExpressions.VariableLocal;

            StringBuilder textBetween = new StringBuilder();
            bool buildText = false;

            bool mayExec = true;
            int brackets = 0;

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                if (ZincEnabled == true)
                {
                    if (line.Contains("{") == true)
                    {
                        brackets++;
                    }

                    if (line.Contains("}") == true)
                    {
                        brackets--;
                    }
                }

                if (findingMethodEnd == false)
                {
                    Match methodMatch = Regex.Match(line, methodPattern);

                    if (methodMatch.Success == true)
                    {
                        foundMethod.StartLine = currentLineIndex;
                        foundMethod.Name = methodMatch.Groups["name"].Value;
                        foundMethod.Arguments = methodMatch.Groups["arguments"].Value;
                        foundMethod.Returns = methodMatch.Groups["returns"].Value;

                        string[] arguments = foundMethod.Arguments.Split(',');
                        Array.ForEach(arguments, argument =>
                        {
                            argument = argument.Trim();
                            if (argument != "nothing" && argument != "void")
                            {
                                argument = argument.Replace("  ", " ");
                                string[] splittedArgument = argument.Split(' ');
                                if (splittedArgument.Length == 2)
                                {
                                    foundMethod.Variables.Add(new MethodVariable
                                    {
                                        StartLine = currentLineIndex,
                                        EndLine = currentLineIndex,
                                        Parent = foundMethod,
                                        Argument = true,
                                        Value = "",
                                        Type = splittedArgument[0],
                                        Name = splittedArgument[1]
                                    });
                                }
                            }
                        });

                        bool isPublic = false;
                        bool isPrivate = false;
                        bool isConstant = false;

                        if (methodMatch.Groups["public"].Value == "public")
                        {
                            foundMethod.Modifiers = Modifiers.Public;
                            isPublic = true;
                        }

                        if (methodMatch.Groups["private"].Value == "private")
                        {
                            foundMethod.Modifiers = isPublic == true ? Modifiers.Public | Modifiers.Private : Modifiers.Private;
                            isPrivate = true;
                        }

                        if (methodMatch.Groups["constant"].Value == "constant")
                        {
                            foundMethod.Modifiers = isPublic == true && isPrivate == true ? Modifiers.Public |
                                Modifiers.Private | Modifiers.Constant : isPrivate == true ? Modifiers.Private |
                                Modifiers.Constant : isPublic == true ? Modifiers.Public | Modifiers.Constant :
                                Modifiers.Constant;

                            isConstant = true;
                        }

                        if (methodMatch.Groups["static"].Value == "static")
                        {
                            foundMethod.Modifiers = isPublic == true && isPrivate == true && isConstant == true ?
                                Modifiers.Public | Modifiers.Private | Modifiers.Constant | Modifiers.Static :
                                isPrivate == true ? Modifiers.Private | Modifiers.Static : isPublic == true ?
                                Modifiers.Public | Modifiers.Static : isConstant == true ? Modifiers.Constant |
                                Modifiers.Static : isPrivate == true && isConstant == true ? Modifiers.Private |
                                Modifiers.Constant | Modifiers.Static : isPublic == true && isConstant == true ?
                                Modifiers.Public | Modifiers.Constant | Modifiers.Static : Modifiers.Static;
                        }

                        findingMethodEnd = true;
                        buildText = true;
                    }
                }
                else
                {
                    if (((ZincEnabled == true) && brackets > 0) || (line.Trim().StartsWith("//") == true))
                    {
                        mayExec = false;
                    }

                    if (mayExec == true)
                    {
                        Match varMatch = Regex.Match(line, methodVariablePattern);

                        if (varMatch.Success == true)
                        {
                            MethodVariable variable = new MethodVariable
                            {
                                StartLine = currentLineIndex,
                                EndLine = currentLineIndex,
                                Name = varMatch.Groups["name"].Value,
                                Type = varMatch.Groups["type"].Value,
                                Value = varMatch.Groups["value"].Value
                            };

                            foundMethod.Variables.Add(variable);
                        }

                        Match endMethodMatch = Regex.Match(line, methodEndPattern);

                        if (endMethodMatch.Success == true)
                        {
                            foundMethod.EndLine = currentLineIndex;
                            findingMethodEnd = false;
                            buildText = false;

                            textBetween = new StringBuilder();
                            listFoundMethods.Add(foundMethod);

                            foundMethod = new Method();
                        }
                    }

                    mayExec = true;
                }

                currentLineIndex++;

                if (buildText == true)
                {
                    textBetween.Append(line);
                    textBetween.AppendLine();
                }
            });

            return listFoundMethods;
        }

        internal MemberCollection ParseMembers(string text, int currentIndex)
        {
            MemberCollection listFoundMembers = new MemberCollection();

            string memberPattern = ZincEnabled == true ? RegularExpressions.VariableStructZinc : RegularExpressions.VariableStruct;

            int currentLineIndex = currentIndex;
            string[] separators = new string[] { Environment.NewLine };
            string[] splittedText = text.Split(separators, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                Match memberMatch = Regex.Match(line, memberPattern);

                if (memberMatch.Success == true && line.Trim().StartsWith("//") == false)
                {
                    Member member = new Member { StartLine = currentLineIndex, EndLine = currentLineIndex, Name = memberMatch.Groups["name"].Value, Type = memberMatch.Groups["type"].Value, Value = memberMatch.Groups["value"].Value };
                    bool isPublic = false;
                    bool isPrivate = false;
                    bool isConstant = false;

                    if (memberMatch.Groups["public"].Value == "public")
                    {
                        member.Modifiers = Modifiers.Public;
                        isPublic = true;
                    }

                    if (memberMatch.Groups["private"].Value == "private")
                    {
                        member.Modifiers = isPublic == true ? Modifiers.Public | Modifiers.Private : Modifiers.Private;
                        isPrivate = true;
                    }

                    if (memberMatch.Groups["constant"].Value == "constant")
                    {
                        isConstant = true;
                        member.Modifiers = isPublic == true && isPrivate == true ? Modifiers.Public |
                            Modifiers.Private | Modifiers.Constant : isPrivate == true ?
                            Modifiers.Private | Modifiers.Constant : isPublic == true ?
                            Modifiers.Public | Modifiers.Constant : Modifiers.Constant;
                    }

                    if (memberMatch.Groups["static"].Value == "static")
                    {
                        member.Modifiers = isPublic == true && isPrivate == true && isConstant == true ?
                            Modifiers.Public | Modifiers.Private | Modifiers.Constant | Modifiers.Static :
                            isPrivate == true ? Modifiers.Private | Modifiers.Static : isPublic == true ?
                            Modifiers.Public | Modifiers.Static : isConstant == true ? Modifiers.Constant |
                            Modifiers.Static : isPrivate == true && isConstant == true ? Modifiers.Private |
                            Modifiers.Constant | Modifiers.Static : isPublic == true && isConstant == true ?
                            Modifiers.Public | Modifiers.Constant | Modifiers.Static : Modifiers.Static;
                    }

                    listFoundMembers.Add(member);
                }

                currentLineIndex++;
            });

            return listFoundMembers;
        }

        internal string GetLineWithoutComment(string text, string originalText, int currentIndex)
        {
            bool removeLastChar = false;

            string lineText = text.Trim();
            string newLineText = "";

            int index = 0;

            string temp = "";
            foreach (char ch in lineText)
            {
                temp += ch;
                if (temp.Contains("//") == true)
                {
                    if (newLineText.Length > 0)
                    {
                        newLineText = newLineText.Substring(0, newLineText.Length - 1);
                    }

                    temp = "";
                    break;
                }
                else if (temp.Contains("/*") == true)
                {
                    IsInCommentBlock = true;
                    removeLastChar = true;

                    int endIndex = originalText.IndexOf("*/", currentIndex + index);

                    if (endIndex > -1)
                    {
                        originalText.Remove(currentIndex + index, endIndex - (currentIndex + index) + 2);
                    }

                    temp = "";
                }

                if (IsInCommentBlock == false)
                {
                    newLineText += ch;
                }

                if (removeLastChar == true)
                {
                    if (newLineText.Length > 0)
                    {
                        newLineText = newLineText.Substring(0, newLineText.Length - 1);
                    }
                }

                removeLastChar = false;
                index++;
            }

            return newLineText;
        }

        internal string RemoveComments(string text)
        {
            string newText = "";
            int index = 0;
            int lineIndex = 0;
            string[] splittedText = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            Array.ForEach(splittedText, line =>
            {
                index += splittedText[lineIndex].Length;
                string lineText = GetLineWithoutComment(line, text, index);
                newText += String.Format("{0}{1}", lineText, Environment.NewLine);
                lineIndex++;
            });

            return newText;
        }

        public JParser()
        {
            Output = new List<TreeNode>();
            Libraries = new LibraryCollection();
            Scopes = new ScopeCollection();
            Structs = new StructCollection();
            Functions = new FunctionCollection();
            Globals = new GlobalsCollection();
        }

        public JParser(JParser parser)
        {
            Output = new List<TreeNode>(parser.Output.Count);
            Output = new List<TreeNode>(parser.Output);
            Libraries = new LibraryCollection(parser.Libraries);
            Scopes = new ScopeCollection(parser.Scopes);
            Structs = new StructCollection(parser.Structs);
            Functions = new FunctionCollection(parser.Functions);
            Globals = new GlobalsCollection(parser.Globals);
            Text = parser.Text;
            vJassEnabled = parser.vJassEnabled;
            ZincEnabled = parser.ZincEnabled;
        }
    }
}
